package cn.xd.security.provider;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.xd.base.application.service.command.GlobalAppUserAppService;
import cn.xd.base.application.service.query.GlobalAppUserQueryAppService;
import cn.xd.base.domain.bean.command.AppUserThirdPartyCmd;
import cn.xd.base.domain.bean.command.CreateUserCommand;
import cn.xd.base.domain.entity.GlobalAppUser;
import cn.xd.common.enums.auth.AppUserTypeEnum;
import cn.xd.common.enums.auth.ThirdPartyTypeEnum;
import cn.xd.common.exception.BizException;
import cn.xd.security.token.CustomerUserWxMiniToken;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class CustomerUserWxMiniAppProvider extends DaoAuthenticationProvider {

    @Resource(name = "CustomerUserDetailServiceImpl")
    private UserDetailsService userDetailsService;
    @Resource(name = "NoPasswordEncoder")
    private PasswordEncoder passwordEncoder;
    @Resource(name = "MainMiniAppService")
    private WxMaService wxMaService;
    @Resource
    private GlobalAppUserQueryAppService globalAppUserQueryAppService;
    @Resource
    private GlobalAppUserAppService globalAppUserAppService;

    //构造方法注入MyUserDetailsService和MyPasswordEncoder
    @Override
    public void doAfterPropertiesSet() {
        setPasswordEncoder(passwordEncoder);
        setUserDetailsService(userDetailsService);
    }

    /**
     * 通过微信code获取手机号
     *
     * @param code
     * @param wxMaService
     * @return
     */
    private String getPhoneByMiniAppCode(String code, WxMaService wxMaService) {
        String phone = StrUtil.EMPTY;
        try {
            WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(code);
            if (ObjUtil.isEmpty(phoneNoInfo)) {
                log.error("未能获取手机号");
                throw new BizException("未能获取手机号");
            }
            phone = phoneNoInfo.getPhoneNumber();
        } catch (WxErrorException e) {
            log.error("WxErrorException" + e);
            throw new BizException("WxErrorException" + e);
        }
        return phone;
    }

    @Override
    public Authentication authenticate(Authentication authentication) {
        log.info("CustomerUserWxMiniAppProvider");

        CustomerUserWxMiniToken adminUserCaptchaToken = (CustomerUserWxMiniToken) authentication;

        Integer type = ThirdPartyTypeEnum.WECHAT_MINI_APP.getCode();
        Integer appUserType = AppUserTypeEnum.MAIN_ADMIN.getCode();
        String phone = adminUserCaptchaToken.getPhone();
        try {
            Long userId;
            String openId;
            String unionId;

            if (StrUtil.isBlank(adminUserCaptchaToken.getPhone()) && StrUtil.isNotBlank(adminUserCaptchaToken.getPhoneCode())) {
                phone = getPhoneByMiniAppCode(adminUserCaptchaToken.getPhoneCode(), wxMaService);
            }
            GlobalAppUser globalAppUser = globalAppUserQueryAppService.getGeneralUserByPhone(phone).get();
            WxMaJscode2SessionResult session;
            if (ObjUtil.isEmpty(globalAppUser)) {
                session = wxMaService.getUserService().getSessionInfo(adminUserCaptchaToken.getCode());
                CreateUserCommand build = CreateUserCommand.builder()
                        .nickName(IdUtil.getSnowflakeNextIdStr())
                        .gender("0")
                        .avatarUrl("")
                        .phone(phone).fromApp(appUserType).fromId(adminUserCaptchaToken.getFromId())
                        .thirdPartyCmd(AppUserThirdPartyCmd.builder().appUserType(appUserType).type(type)
                                .openId(session.getOpenid()).unionId(session.getUnionid())
                                .build())
                        .fromWay(adminUserCaptchaToken.getFromWay()).build();
                userId = globalAppUserAppService.register(build);
            } else {
                session = wxMaService.getUserService().getSessionInfo(adminUserCaptchaToken.getCode());
                userId = globalAppUser.getId().getValue();
                //没有code，说明appUserThirdParty已经存在,无需新增
                if (StrUtil.isNotBlank(adminUserCaptchaToken.getPhoneCode())) {
                    globalAppUserAppService.addAppUserThirdParty(globalAppUser, session.getOpenid(), session.getUnionid(), type, appUserType);
                }
            }
            openId = session.getOpenid();
            unionId = session.getUnionid();
        } catch (WxErrorException e) {
            log.error("WxErrorException" + e);
            throw new BizException("WxErrorException" + e);
        }
        Authentication authenticate = null;
        return super.authenticate(new UsernamePasswordAuthenticationToken(phone, authentication.getCredentials()));
    }

    /**
     * 判断只有传入UserAuthenticationToken的时候才使用这个Provider
     * supports会在AuthenticationManager层被调用
     *
     * @param authentication
     * @return
     */
    public boolean supports(Class<?> authentication) {
        return CustomerUserWxMiniToken.class.isAssignableFrom(authentication);
    }
}
